#include "mainwindow.h"
#include "ui_mainwindow.h"

#include "qcustomplot.h"
#include "muParser/muParser.h"
#include "tspline.h"

using namespace mu;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

   // ui->pushButton_2->setHidden(true);

    table = new QTableWidget(0);
    table->setColumnCount(11);
    table->setWindowTitle("Results of interpolation");
    QStringList headers;
    headers.append("i");
    headers.append("x");
    headers.append("f(x)");
    headers.append("S(x)");
    headers.append("|S(x)-f(x)|");
    headers.append("f'(x)");
    headers.append("S'(x)");
    headers.append("|S'(x)-f'(x)|");
    headers.append("f''(x)");
    headers.append("S''(x)");
    headers.append("|S''(x)-f''(x)|");
    table->setHorizontalHeaderLabels(headers);
    table->verticalHeader()->hide();
    table->setColumnWidth(0,35);

    splineTable = new QTableWidget(0);
    splineTable->setColumnCount(7);
    splineTable->setWindowTitle("Cubic splines coefficients");
    headers.clear();
    headers.append("i");
    headers.append("x_i");
    headers.append("x_(i+1)");
    headers.append("a_i");
    headers.append("b_i");
    headers.append("c_i");
    headers.append("d_i");
    splineTable->setHorizontalHeaderLabels(headers);
    splineTable->verticalHeader()->hide();
    splineTable->setColumnWidth(0,35);
}

MainWindow::~MainWindow()
{
    delete table;
    delete splineTable;
    delete ui;
}

bool MainWindow::event(QEvent *mEvent)
{
    if (mEvent->type()==QEvent::Close)
    {
        table->close();
        splineTable->close();
        this->close();
        return true;
    }
    return QMainWindow::event(mEvent);
}

void MainWindow::countFunc(QString function, double x0, double xn, int n,  QVector<double>& x,  QVector<double>& y, QVector<double>& yDiff, QVector<double>& ySecondDiff)
{
    double epsForDiff = 1e-6;
    x.clear();
    yDiff.clear();
    ySecondDiff.clear();
    y.clear();
    x.resize(n+1);
    y.resize(n+1);
    yDiff.resize(n+1);
    ySecondDiff.resize(n+1);
    double h = (xn-x0) / n;

    Parser p;
    double variable;
    p.DefineVar("x", &variable);
    p.SetExpr(function.toStdString().c_str());

    x[0]=x0;
    variable=x[0];
    y[0] = p.Eval();
    yDiff[0] = p.Diff(&variable, variable, epsForDiff);
    ySecondDiff[0] = p.SecondDiff(&variable, variable, epsForDiff);

    for (int i=1; i<n+1; ++i)
    {
      x[i] = variable = x[i-1]+h;
      variable=x[i];
      y[i] = p.Eval();
      yDiff[i] = p.Diff(&variable, variable, epsForDiff);
      ySecondDiff[i] = p.SecondDiff(&variable, variable, epsForDiff);

    }
}

void MainWindow::plotFunc(QVector<double>& x, QVector<double>& y, QPen color)
{
    ui->graphWidget->addGraph();

    ui->graphWidget->graph(ui->graphWidget->graphCount()-1)->setData(x, y);
    // give the axes some labels:
    ui->graphWidget->xAxis->setLabel("x");
    ui->graphWidget->yAxis->setLabel("y");
    ui->graphWidget->graph(ui->graphWidget->graphCount()-1)->setPen(color);
    //ui->graphWidget->xAxis->setRange(x[0]-0.5,x[x.size()+1]+0.5);
    //ui->graphWidget->yAxis->setRange(-2.5,2.5);
    //ui->graphWidget->graph(ui->graphWidget->graphCount()-1)->rescaleAxes(true);
    //ui->graphWidget->replot();
}

void MainWindow::on_pushButton_clicked()
{    
    splineTable->hide();
    table->hide();
    ui->graphWidget->clearGraphs();
    double s0 = ui->lineEdit_S0->text().toDouble();
    double sn = ui->lineEdit_Sn->text().toDouble();
    double x0 = ui->edit_x0->value();
    double xn = ui->edit_xn->value();
    int n = ui->spinBox->value();
    QString function = ui->lineEdit->text();

    QVector<double> x(n+1), y(n+1), yDiff, ySecondDiff;
    countFunc(function, x0, xn, n,  x, y, yDiff, ySecondDiff);

    TSpline spline;
    spline.createSpline(x,y,n,s0,sn);

    if (ui->checkBox->checkState() == Qt::Checked)
    {
        splineTable->setRowCount(n);
        for (int i = 1; i<n+1; i++)
        {
            splineTable->setItem(i-1,0,new QTableWidgetItem(QString::number(i)));
            splineTable->setItem(i-1,1,new QTableWidgetItem(QString::number(x[i-1])));
            splineTable->setItem(i-1,2,new QTableWidgetItem(QString::number(x[i])));
            splineTable->setItem(i-1,3,new QTableWidgetItem(QString::number(spline.a[i])));
            splineTable->setItem(i-1,4,new QTableWidgetItem(QString::number(spline.b[i])));
            splineTable->setItem(i-1,5,new QTableWidgetItem(QString::number(spline.c[i])));
            splineTable->setItem(i-1,6,new QTableWidgetItem(QString::number(spline.d[i])));
        }
    }

    int multiplier = 4;
    QVector<double> splinesX(multiplier*n+1);
    QVector<double> splinesY(multiplier*n+1);
    QVector<double> splinesYDiff(multiplier*n+1);
    QVector<double> splinesYSecondDiff(multiplier*n+1);
    splinesX[0] = x[0];
    splinesY[0] = spline.getValue(splinesX[0]);    
    splinesYDiff[0] = spline.getDerivativeValue(splinesX[0]);
    splinesYSecondDiff[0] = spline.getSecondDerivativeValue(splinesX[0]);
    double hSpline = (x[n]-x[0])/(multiplier*n);
    for (int i=1; i<multiplier*n+1; i++)
    {
        splinesX[i] = splinesX[i-1]+hSpline;
        splinesY[i] = spline.getValue(splinesX[i]);
        splinesYDiff[i] = spline.getDerivativeValue(splinesX[i]);
        splinesYSecondDiff[i] = spline.getSecondDerivativeValue(splinesX[i]);
    }

    countFunc(function, x0, xn, multiplier*n,  x, y, yDiff, ySecondDiff);

    table->setRowCount(multiplier*n+1);
    double maxDev = -1.0, maxDevDer = -1.0, maxDevSecDer = -1.0;
    for (int i=0; i<multiplier*n+1; i++)
    {
        if (fabs(y[i]-splinesY[i])>maxDev)
            maxDev = fabs(y[i]-splinesY[i]);
        if (fabs(yDiff[i]-splinesYDiff[i])>maxDevDer)
            maxDevDer = fabs(yDiff[i]-splinesYDiff[i]);
        if (fabs(ySecondDiff[i]-splinesYSecondDiff[i])>maxDevSecDer)
            maxDevSecDer = fabs(ySecondDiff[i]-splinesYSecondDiff[i]);

        if (ui->checkBox->checkState() == Qt::Checked)
        {
            table->setItem(i,0,new QTableWidgetItem(QString::number(i)));
            table->setItem(i,1,new QTableWidgetItem(QString::number(splinesX[i])));
            table->setItem(i,2,new QTableWidgetItem(QString::number(y[i])));
            table->setItem(i,3,new QTableWidgetItem(QString::number(splinesY[i])));
            table->setItem(i,4,new QTableWidgetItem(QString::number(fabs(y[i]-splinesY[i]))));
            table->setItem(i,5,new QTableWidgetItem(QString::number(yDiff[i])));
            table->setItem(i,6,new QTableWidgetItem(QString::number(splinesYDiff[i])));
            table->setItem(i,7,new QTableWidgetItem(QString::number(fabs(yDiff[i]-splinesYDiff[i]))));
            table->setItem(i,8,new QTableWidgetItem(QString::number(ySecondDiff[i])));
            table->setItem(i,9,new QTableWidgetItem(QString::number(splinesYSecondDiff[i])));
            table->setItem(i,10,new QTableWidgetItem(QString::number(fabs(ySecondDiff[i]-splinesYSecondDiff[i]))));
        }
    }
    if (ui->checkBox->checkState() == Qt::Checked)
    {
        table->show();
        splineTable->show();
    }

    ui->label_Dev->setText(QString::number(maxDev));
    ui->label_DevDer->setText(QString::number(maxDevDer));
    ui->label_DevSecDer->setText(QString::number(maxDevSecDer));

    if (ui->radioButton_func->isChecked())
    {
        plotFunc(splinesX, splinesY, QPen(Qt::red));
        plotFunc(x,y);
    }
    if (ui->radioButton_fDer->isChecked())
    {
        plotFunc(splinesX, splinesYDiff, QPen(Qt::red));
        plotFunc(x,yDiff);
    }
    if (ui->radioButton_sDer->isChecked())
    {
        plotFunc(splinesX, splinesYSecondDiff, QPen(Qt::red));
        plotFunc(x,ySecondDiff);
    }
    ui->graphWidget->graph(0)->rescaleAxes();
    ui->graphWidget->graph(1)->rescaleAxes(true);
    ui->graphWidget->replot();
}

void MainWindow::on_pushButton_2_clicked()
{    
    ui->graphWidget->clearGraphs();

    int n = 4;

    QVector<double> x(n+1), y(n+1);
    x[0]=-1;
    double s0=-6;
    double sn=6;
    for (int i=1; i<n+1; ++i)
    {
      x[i] =  x[i-1]+0.5;
    }
    y[0]=-2;y[1]=-0.625;y[2]=0;y[3]=-y[1];y[4]=-y[0];        

    plotFunc(x,y);
    ui->graphWidget->graph(0)->rescaleAxes();
    //ui->graphWidget->xAxis->setRange(x[0]-0.1,x[n]);

    TSpline spline;
    spline.createSpline(x,y,n,s0,sn);

    int multiplier = 30;
    QVector<double> splinesX(multiplier*n+1);
    QVector<double> splinesY(multiplier*n+1);
    splinesX[0] = x[0];
    splinesY[0] = spline.getValue(splinesX[0]);
    double hSpline = (x[n]-x[0])/(multiplier*n);
    for (int i=1; i<multiplier*n+1; i++)
    {
        splinesX[i] = splinesX[i-1]+hSpline;
        splinesY[i] = spline.getValue(splinesX[i]);
    }

    plotFunc(splinesX, splinesY,QPen(Qt::red));
    ui->graphWidget->graph(1)->rescaleAxes(true);
    ui->graphWidget->replot();

    if (ui->checkBox->checkState() == Qt::Checked)
    {
        splineTable->setRowCount(n);
        for (int i = 1; i<n+1; i++)
        {
            splineTable->setItem(i-1,0,new QTableWidgetItem(QString::number(i)));
            splineTable->setItem(i-1,1,new QTableWidgetItem(QString::number(x[i-1])));
            splineTable->setItem(i-1,2,new QTableWidgetItem(QString::number(x[i])));
            splineTable->setItem(i-1,3,new QTableWidgetItem(QString::number(spline.a[i])));
            splineTable->setItem(i-1,4,new QTableWidgetItem(QString::number(spline.b[i])));
            splineTable->setItem(i-1,5,new QTableWidgetItem(QString::number(spline.c[i])));
            splineTable->setItem(i-1,6,new QTableWidgetItem(QString::number(spline.d[i])));
        }
        splineTable->show();
    }
}
